#!/usr/bin/env python

import datetime
import os
import os.path
import re
import sys
import urllib

import cherrypy
import HTMLTemplate
sys.path.insert(0, 'secore')
import secore

template_dir = 'templates'
_templateCache = {}
def getTemplate (tname, renderfn):
    """Get a template from the template cache.

    """
    path = os.path.join (template_dir, tname + '.html')
    s = os.stat (path)
    try:
        cached = _templateCache[tname + str(renderfn)]
        if cached[1] == s.st_mtime:
            return cached[0]
    except KeyError:
        pass

    f = open (path)
    html = f.read()
    f.close()
    t = HTMLTemplate.Template (renderfn, html)
    _templateCache[tname] = (t, s.st_mtime)
    return t

_tag_re = re.compile (r'</?(\w+).*?/?>')
def unhtml (body):
    body = body.replace('&#13;', ' ')
    def _sub (mo):
        tagname = mo.group(1).lower()
        if tagname in ('p', 'br', 'div'):
            return ' '
        if tagname in ('a', ):
            return tagname
    return _tag_re.sub (_sub, body)

def getFeedId(item):
    """Get the feed ID, given a result item.

    """
    id = item.data.get('feedlink', [''])[0]
    if id == '':
        raise cherrypy.HTTPError(400, "Can't get feed ID")
    return id

def getFeedTitle(item):
    """Get the feed title, given a result item.

    """
    result = item.data.get('feedtitle', '')
    if len(result) != 0 and len(result[0]) != 0: return result[0]
    result = item.data.get('feeddescription', '')
    if len(result) != 0 and len(result[0]) != 0: return result[0]
    result = item.data.get('feedlink', '')
    if len(result) != 0 and len(result[0]) != 0: return result[0]
    return 'Unknown feed'

def saveparam(saveargs, name, kwargs, defargs, newargs):
    defval = defargs.get(name, None)
    val = newargs.get(name, None)
    if val is None:
        val = kwargs.get(name, None)
    if val is not None and str(val) != str(defval):
        saveargs[name] = str(val)

def saveparams(kwargs, defargs, **newargs):
    saveargs = {}

    saveparam(saveargs, 'id', kwargs, defargs, newargs)
    saveparam(saveargs, 'topdoc', kwargs, defargs, newargs)
    saveparam(saveargs, 'hitsperpage', kwargs, defargs, newargs)
    saveparam(saveargs, 'query', kwargs, defargs, newargs)
    saveparam(saveargs, 'status', kwargs, defargs, newargs)

    return urllib.urlencode(saveargs)

def getIntArg(name, kwargs, defargs):
    return int(kwargs.get(name, defargs[name]))

def round(val, chunksize):
    if val < 0: val = 0
    return (val // chunksize) * chunksize

class Search:
    def __init__(self, dbpath):
        self.dbpath = dbpath

    @cherrypy.expose
    def index(self, **kwargs):
        cherrypy.response.headers['Content-Type'] = 'text/html;charset=utf-8'
        t = getTemplate('index', self.renderIndex)
        return t.render(kwargs)

    @cherrypy.expose
    def feed(self, **kwargs):
        cherrypy.response.headers['Content-Type'] = 'text/html;charset=utf-8'
        t = getTemplate('feed', self.renderFeed)
        return t.render(kwargs)

    def renderIndex(self, t, kwargs):
        conn = secore.SearchConnection(dbpath)
        results = conn.search(conn.query_all(), 0, conn.get_doccount(), collapse='feedlink', sortby='-time')
        t.feedlist.repeat(self.renderIndexItem, results, conn, results)

    def renderIndexItem(self, t, item, conn, results):
        link = item.data.get('feedlink', [''])[0]
        q = conn.query_field('feedlink', link)
        results = conn.search(q, 0, 0, checkatleast=conn.get_doccount())
        docs_in_category = results.matches_lower_bound

        q2 = conn.query_composite(conn.OP_AND, (q, conn.query_field('status', 'R')))
        results = conn.search(q2, 0, 0, checkatleast=conn.get_doccount())
        docs_read = results.matches_lower_bound

        t.feedcount.content = "%d (%d)" % (docs_in_category, docs_in_category - docs_read)

        t.feedlink.atts['href'] = 'feed?id=%s' % urllib.quote_plus(getFeedId(item))
        t.feedlink.content = unhtml(getFeedTitle(item))
        t.feeddesc.content = unhtml(item.data.get('feeddescription', [''])[0])[:80]
        t.feedupdate.atts['href'] = 'update?id=%s' % urllib.quote_plus(getFeedId(item))

    def renderFeed(self, t, kwargs):
        conn = secore.SearchConnection(dbpath)
        id = kwargs.get('id', None)

        defargs = {'topdoc': 0, 'hitsperpage': 10}
        topdoc = getIntArg('topdoc', kwargs, defargs)
        hitsperpage = getIntArg('hitsperpage', kwargs, defargs)
        query = kwargs.get('query', None)
        status = kwargs.get('status', None)

        if id is None:
            if query is None:
                q = conn.query_all()
            else:
                q = conn.query_parse(query)
        else:
            q = conn.query_field('feedlink', id)
            if query is not None:
                q = conn.query_filter(conn.query_parse(query), q)

        if status is not None:
            if status[0] == '!':
                q = conn.query_filter(q, conn.query_field('status', status[1:]), exclude=True)
            else:
                q = conn.query_filter(q, conn.query_field('status', status))

        # Run the search, restricted
        q2 = conn.query_filter(q, conn.query_field('status', 'R'))
        results = conn.search(q2, 0, 0, checkatleast=conn.get_doccount())
        docs_read = results.matches_lower_bound

        results = conn.search(q, topdoc, topdoc + hitsperpage, checkatleast=conn.get_doccount(), sortby='-time')
        t.artnums.content = '%d-%d' % (results.startrank + 1, results.endrank)
        t.artcount.content = '%d' % results.matches_lower_bound

        if docs_read != results.matches_lower_bound:
            t.artread.content = ' (%d unread)' % (results.matches_lower_bound - docs_read)

        if results.matches_lower_bound == 0:
            return # FIXME - should redirect to an appropriate empty page.

        if results.matches_lower_bound > results.endrank:
            t.nextlink.atts['href'] = 'feed?%s' % saveparams(kwargs, defargs, topdoc=str(round(topdoc + hitsperpage, hitsperpage)))
        else:
            t.nextlink.omit()

        if results.startrank > 0:
            t.prevlink.atts['href'] = 'feed?%s' % saveparams(kwargs, defargs, topdoc=str(round(topdoc - hitsperpage, hitsperpage)))
        else:
            t.prevlink.omit()

        if id is None:
            title = "All feeds"
        else:
            title = unhtml(getFeedTitle(results[0]))
        t.feedtitle.content = title
        t.feedtitle2.content = title
        t.entries.repeat(self.renderFeedItem, results, conn, results)

    def renderFeedItem(self, t, item, conn, results):
        itemtime = item.data.get('time', [''])[0]
        dt = datetime.datetime.fromtimestamp(int(itemtime))
        t.date.content = str(dt)
        t.title.content = item.data.get('title', [''])[0]
        t.entry.raw = item.data.get('summary', [''])[0]
        if 'R' in item.data.get('status', []):
            t.status.content = 'Read'
        else:
            t.status.content = 'Unread'

dbpath = 'feeddb'
search = Search(dbpath)

cherrypy.quickstart(search)
